 
  

 






<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
<html>

<!-- Mirrored from www.javapractices.com/topic/TopicAction.do;jsessionid=4FCCB481C702D708A7360133D128E359?Id=160 by HTTrack Website Copier/3.x [XR&CO'2010], Sun, 12 Jun 2011 17:27:18 GMT -->
<!-- Added by HTTrack --><meta http-equiv="content-type" content="text/html;charset=UTF-8"><!-- /Added by HTTrack -->
<head>
 <title>
  Java Practices -> Timers
 </title>
 <link rel="stylesheet" type="text/css" href="../stylesheet8.css" media="all">
 
 <link rel="shortcut icon" href='../images/favicon.ico' type="image/vnd.microsoft.icon">
 <meta name="description" content="Concise presentations of java programming practices, tasks, and conventions, amply illustrated with syntax highlighted code examples.">
 
 <meta name='keywords' content='AboutAction,EventQueue,FetchQuotesAction,SwingUtilities,TimerTask,swing,timer,java,java programming,java practices,java idiom,java style,java design patterns,java coding conventions,'>
 
 
</head>
 
<body>


<div class='menu-bar'>
 
  <a href='../home/HomeAction.html' title='Table of Contents'>Home</a> |
  <a href='../vote/VoteSummaryAction-2.html' title='View Poll Results'>Poll</a> |
   
  <A href='../feedback/FeedbackAction451f-2.html?Operation=Show' title='Send Your Feedback'>Wiki</a> |
  <b><a href='../source/SourceAction-2.html' title='Grab Source Code'>Source Code</a></b><IMG class='no-margin' SRC="../images/goldstar.gif" ALT=""> |

  <a href='http://www.web4j.com/Java_Web_Application_Framework_Overview.jsp?From=1' title='Free Download - Java Web Application Framework'><b>WEB4J</b></a> |
  
  <a href='http://www.date4j.net/' title='Replacement for java.util.Date'><b>DATE4J</b></a> |

   <a href='../references/ReferencesAction-2.html' title='References'>Links</a>
   
  <form action='http://www.javapractices.com/search/SearchAction.do' method='get' class='search-form'>
   <input type='text' name='SearchTerms' value="" size=12 maxlength=50 class='search'>
   <input type='submit' value="Search">
  </form>
 
</div>

<P>



  

 






<p class="display-messages">

 

 

</p>


<div class="main-layout">
 
   

 




<div class='page-title'>Timers</div>

<div class='main-body'>
 
<br><a href="http://java.sun.com/docs/books/tutorial/uiswing/misc/timer.html">Timers</a>
are used to perform actions periodically, after an initial delay, or both.
Also, actions can be performed only once, if desired.
<p>Here are some reminders regarding the two flavors of timers, as used
in Swing applications :
<p><tt><a href="http://java.sun.com/javase/6/docs/api/javax/swing/Timer.html">javax.swing.Timer</a></tt>
<ul>
<li>
suitable for simpler cases, using low numbers of timers (say less than
a dozen)</li>

<li>
runs <tt><a href="http://java.sun.com/javase/6/docs/api/java/awt/event/ActionListener.html">ActionListener</a></tt>
objects on the event dispatch thread</li>

<li>
can directly update the GUI, without using <tt><a href="http://java.sun.com/javase/6/docs/api/java/awt/EventQueue.html#invokeLater(java.lang.Runnable)">EventQueue.invokeLater</a></tt></li>

<li>
if the task runs entirely in the event dispatch thread (that is, if it
does not spawn a worker thread), then the GUI will remain responsive only
if the task does not take very long (say under 300 milliseconds)</li>
</ul>
<tt><a href="http://java.sun.com/javase/6/docs/api/java/util/Timer.html">java.util.Timer</a></tt>
<ul>
<li>
more scalable than <tt>javax.swing.Timer</tt>, and with additional scheduling
features</li>

<li>
runs <tt><a href="http://java.sun.com/javase/6/docs/api/java/util/TimerTask.html">TimerTask</a></tt>
objects on a private thread</li>

<li>
needs to use <tt>EventQueue.invokeLater</tt> to update the GUI</li>
</ul>

<b>Example 1</b>
<P>For a simple example using <tt>java.util.Timer</tt> and <tt>java.util.TimerTask</tt> in a non-Swing context, please see this <a href='TopicActionc150-2.html?Id=54'>related topic</a>.

<P><b>Example 2</b>
<p>Here, an About Box includes a <tt>JLabel</tt> which displays the current
object heap size. This <tt>JLabel</tt> is updated every few seconds, to
reflect any activity of the garbage collector. (Note that the timer itself
does not itself request garbage collection.)
<p>This task is not an intensive one (see <tt>getHeapSize</tt> below),
so it may be performed directly on the event dispatch thread without causing
the GUI to become unresponsive. That is, a Swing timer may be used.
<br>
<PRE>

<span class='keyword'>package</span> hirondelle.stocks.help;

<span class='keyword'>import</span> java.awt.event.*;
<span class='keyword'>import</span> java.awt.*;
<span class='keyword'>import</span> javax.swing.*;
<span class='keyword'>import</span> java.util.*;
<span class='keyword'>import</span> java.util.logging.*;

<span class='keyword'>import</span> hirondelle.stocks.util.Consts;
<span class='keyword'>import</span> hirondelle.stocks.util.Args;
<span class='keyword'>import</span> hirondelle.stocks.util.FileUtil;
<span class='keyword'>import</span> hirondelle.stocks.util.ui.UiConsts;
<span class='keyword'>import</span> hirondelle.stocks.util.ui.UiUtil;
<span class='keyword'>import</span> hirondelle.stocks.preferences.LoggingPreferencesEditor;
<span class='keyword'>import</span> hirondelle.stocks.util.Util;

<span class='comment'>/**
* Display a modal dialog, centered on the main window, which
* contains general information about both this application and 
* the system on which it is running.
*
*&lt;P&gt; The system information includes a running snapshot of the 
* object heap size. A button is provided to coax the JVM to 
* perform garbage collection.
*/</span>
<span class='keyword'>public</span> <span class='keyword'>final</span> <span class='keyword'>class</span> AboutAction <span class='keyword'>extends</span> AbstractAction {

  <span class='comment'>/**
  * Constructor. 
  * 
  * @param aFrame parent window to which this dialog is attached.
  */</span>
  <span class='keyword'>public</span> AboutAction(JFrame aFrame) {
    <span class='keyword'>super</span>(<span class='literal'>"About "</span> + Consts.APP_NAME);
    Args.checkForNull(aFrame);
    fFrame = aFrame;
    putValue(SHORT_DESCRIPTION, <span class='literal'>"About this application"</span>);
    putValue(LONG_DESCRIPTION, <span class='literal'>"Displays details regarding the StocksMonitor application."</span>);
    putValue(MNEMONIC_KEY, <span class='keyword'>new</span> Integer(KeyEvent.VK_A) );    
  }

  <span class='keyword'>public</span> <span class='keyword'>void</span> actionPerformed(ActionEvent e) {
    fLogger.info(<span class='literal'>"Showing the about box."</span>);    
    showAboutBox();
  }
  
  <span class='comment'>// PRIVATE //
</span>  <span class='keyword'>private</span> JFrame fFrame;
  
  <span class='comment'>/**
  * Displays the size of the object heap.
  *
  * In a typical GUI application, the breakdown for memory consumption is roughly
  * &lt;ul&gt;
  * &lt;li&gt; classes - 70%
  * &lt;li&gt; objects - 15%
  * &lt;li&gt; other  - 15%
  *&lt;/ul&gt;
  *
  * There is no API for displaying total memory use. The memory consumed by objects,
  * however, is available, and is displayed by this label; the display is updated 
  * periodically.
  */</span>
  <span class='keyword'>private</span> JLabel fObjectHeapSize;
  
  <span class='comment'>/** Periodically updates the display of &lt;tt&gt;fObjectHeapSize&lt;/tt&gt;.  */</span>
  <span class='keyword'>private</span> javax.swing.Timer fTimer;
  <span class='keyword'>private</span> ActionListener fHeapSizeUpdater;
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>final</span> <span class='keyword'>int</span> UPDATE_FREQ = <span class='literal'>2</span> * Consts.MILLISECONDS_PER_SECOND;
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>final</span> <span class='keyword'>long</span> SLEEP_INTERVAL = <span class='literal'>100</span>;
  
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>final</span> String ABOUT_TEXT_FILE = <span class='literal'>"About.txt"</span>;
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>final</span> Dimension ABOUT_TEXT_SIZE = <span class='keyword'>new</span> Dimension(<span class='literal'>100</span>,<span class='literal'>250</span>);
  
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>final</span> Logger fLogger = Util.getLogger(AboutAction.<span class='keyword'>class</span>);

  <span class='keyword'>private</span> <span class='keyword'>void</span> showAboutBox(){
    JTabbedPane aboutPane = <span class='keyword'>new</span> JTabbedPane();
    aboutPane.addTab( <span class='literal'>"About"</span> , getAboutPanel() );
    aboutPane.setMnemonicAt(<span class='literal'>0</span>, KeyEvent.VK_A);
    aboutPane.addTab( <span class='literal'>"System Info"</span> , getSystemInfoPanel() );
    aboutPane.setMnemonicAt(<span class='literal'>1</span>, KeyEvent.VK_S);

    startHeapSizeTimer();
    
    Icon image =  UiUtil.getImageIcon(<span class='literal'>"xray-small.jpg"</span>, <span class='keyword'>this</span>.getClass()) ;
    String title = UiUtil.getDialogTitle(<span class='literal'>"About"</span>);
    JOptionPane.showMessageDialog(fFrame, aboutPane, title, JOptionPane.OK_OPTION, image);
    
    stopHeapSizeTimer();
  }
  
  <span class='keyword'>private</span> JComponent getAboutPanel(){
    JPanel aboutPanel = <span class='keyword'>new</span> JPanel();
    aboutPanel.setLayout( <span class='keyword'>new</span> BoxLayout(aboutPanel, BoxLayout.Y_AXIS) );
    
    String appNameAndVersion = Consts.APP_NAME + Consts.SPACE + Consts.APP_VERSION;
    String text = appNameAndVersion + Consts.NEW_LINE + getAboutFileContents();
    JTextArea aboutText = UiUtil.getStandardTextAreaHardNewLines( text );
    JScrollPane scrollPane = <span class='keyword'>new</span> JScrollPane(aboutText);
    scrollPane.setPreferredSize( ABOUT_TEXT_SIZE );
    
    aboutPanel.add( scrollPane );
    <span class='keyword'>return</span> aboutPanel;
  }
  
  <span class='keyword'>private</span> String getAboutFileContents(){
    java.io.InputStream contents = AboutAction.<span class='keyword'>class</span>.getResourceAsStream(ABOUT_TEXT_FILE);
    <span class='keyword'>return</span> FileUtil.getStreamAsString(contents);
  }
  
  <span class='keyword'>private</span> JComponent getSystemInfoPanel(){
    JPanel infoPanel = getStandardPanel();

    Map&lt;String, String&gt; info = <span class='keyword'>new</span> HashMap&lt;String, String&gt;();
    addSysProperty(info, <span class='literal'>"Java Version"</span>,<span class='literal'>"java.version"</span>);
    addSysProperty(info, <span class='literal'>"Java VM"</span>, <span class='literal'>"java.vm.info"</span>);
    addSysProperty(info, <span class='literal'>"Java Home"</span>, <span class='literal'>"java.home"</span>);
    addSysProperty(info, <span class='literal'>"Java Vendor"</span>, <span class='literal'>"java.vendor"</span>);
    addSysProperty(info, <span class='literal'>"User Current Directory"</span>, <span class='literal'>"user.dir"</span> );
    addSysProperty(info, <span class='literal'>"User Home Directory"</span>, <span class='literal'>"user.home"</span> );
    
    StringBuilder osInfo = <span class='keyword'>new</span> StringBuilder();
    osInfo.append( getProperty(<span class='literal'>"os.arch"</span>) );
    osInfo.append( Consts.SPACE);
    osInfo.append( getProperty(<span class='literal'>"os.name"</span>) );
    osInfo.append( Consts.SPACE);
    osInfo.append( getProperty(<span class='literal'>"os.version"</span>) );
    info.put(<span class='literal'>"Operating System"</span>, osInfo.toString());
    
    LoggingPreferencesEditor loggingPrefs = <span class='keyword'>new</span> LoggingPreferencesEditor();
    info.put(<span class='literal'>"Logging Config File"</span>, loggingPrefs.getLogConfigFile().toString() );

    <span class='comment'>//Note that a HashMap is used to populate the tree (bit faster), but a TreeMap is the 
</span>    <span class='comment'>//final version presented to the user (bit slower, but has desired iteration order)
</span>    UiUtil.addSimpleDisplayFields( infoPanel, <span class='keyword'>new</span> TreeMap&lt;String, String&gt;(info) );    
    
    fObjectHeapSize = UiUtil.addSimpleDisplayField(
      infoPanel, 
      <span class='literal'>"Object Heap Size"</span>, 
      getHeapSize(), 
      UiUtil.getConstraints(<span class='literal'>8</span>,<span class='literal'>0</span>),
      <span class='keyword'>true</span>
    );
    fObjectHeapSize.setToolTipText(<span class='literal'>"Total memory consumption is much larger"</span>);
    
    addGarbageCollectionButton(infoPanel);
    <span class='keyword'>return</span> infoPanel;
  }
  
  <span class='keyword'>private</span> <span class='keyword'>void</span> addGarbageCollectionButton(JPanel aInfoPanel){
    JButton collectGarbage = <span class='keyword'>new</span> JButton(<span class='literal'>"Collect Garbage"</span>);   
    collectGarbage.setToolTipText(<span class='literal'>"Request garbage collection by JVM"</span>);
    collectGarbage.setMnemonic(KeyEvent.VK_C);
    collectGarbage.addActionListener( <span class='keyword'>new</span> ActionListener() {
      <span class='keyword'>public</span> <span class='keyword'>void</span> actionPerformed(ActionEvent event) {
        putOutTheGarbage();
        updateHeapSizeDisplay();
      }
    });
    GridBagConstraints constraints = UiUtil.getConstraints(<span class='literal'>9</span>,<span class='literal'>1</span>);
    constraints.insets = <span class='keyword'>new</span> Insets(UiConsts.ONE_SPACE, <span class='literal'>0</span>,<span class='literal'>0</span>,<span class='literal'>0</span>);
    aInfoPanel.add(collectGarbage, constraints );
  }
  
  <span class='comment'>/**
  * Return a &lt;tt&gt;JPanel&lt;/tt&gt; which has a &lt;tt&gt;GridBagLayout&lt;/tt&gt;, and
  * a border as specified in {@link UiUtil#getStandardBorder}.
  */</span>
  <span class='keyword'>private</span> JPanel getStandardPanel(){
    JPanel result  = <span class='keyword'>new</span> JPanel();
    result.setLayout( <span class='keyword'>new</span> GridBagLayout() );
    result.setBorder( UiUtil.getStandardBorder() );
    <span class='keyword'>return</span> result;
  }
  
  <span class='keyword'>private</span> <span class='keyword'>void</span> updateHeapSizeDisplay(){
    fLogger.fine(<span class='literal'>"Updating heap size..."</span>);
    fObjectHeapSize.setText( getHeapSize() );
  }

  <span class='comment'>/** Return a measure of the current heap size in kilobytes.  */</span>
  <span class='keyword'>private</span> String getHeapSize(){
    <span class='keyword'>long</span> totalMemory = Runtime.getRuntime().totalMemory();
    <span class='keyword'>long</span> freeMemory = Runtime.getRuntime().freeMemory();
    Long memoryUseKB = <span class='keyword'>new</span> Long( (totalMemory - freeMemory)/Consts.ONE_KILOBYTE );
    
    StringBuilder result = <span class='keyword'>new</span> StringBuilder();
    result.append( UiUtil.getLocalizedInteger(memoryUseKB) );
    result.append(<span class='literal'>" KB"</span>);
    <span class='keyword'>return</span> result.toString();
  }

  <span class='comment'>/** Periodically update the display of object heap size.  */</span>
  <span class='keyword'>private</span> <span class='keyword'>void</span> startHeapSizeTimer(){
    fHeapSizeUpdater = <span class='keyword'>new</span> ActionListener() {
      <span class='keyword'>public</span> <span class='keyword'>void</span> actionPerformed(ActionEvent evt) {
        updateHeapSizeDisplay();
      }
    };    
    fTimer = <span class='keyword'>new</span> javax.swing.Timer(UPDATE_FREQ, fHeapSizeUpdater);
    fTimer.start();    
    fLogger.fine(<span class='literal'>"Starting timer..."</span>);
  }

  <span class='comment'>/**
  * Must be called when the About Box is closed - otherwise the timer will continue 
  * to operate.
  */</span>
  <span class='keyword'>private</span> <span class='keyword'>void</span> stopHeapSizeTimer(){
    fLogger.fine(<span class='literal'>"Stopping timer..."</span>);
    fTimer.stop(); <span class='comment'>//stops notifying registered listeners
</span>    fTimer.removeActionListener(fHeapSizeUpdater); <span class='comment'>//removes the one registered listener
</span>    fHeapSizeUpdater = <span class='keyword'>null</span>;
    fTimer = <span class='keyword'>null</span>;
  }
  
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>void</span> putOutTheGarbage() {
    collectGarbage();
    collectGarbage();
  }

  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>void</span> collectGarbage() {
    <span class='keyword'>try</span> {
      System.gc();
      Thread.currentThread().sleep(SLEEP_INTERVAL);
      System.runFinalization();
      Thread.currentThread().sleep(SLEEP_INTERVAL);
    }
    <span class='keyword'>catch</span> (InterruptedException ex){
      ex.printStackTrace();
    }
  }
  
  <span class='keyword'>private</span> <span class='keyword'>void</span> addSysProperty(Map&lt;String, String&gt; aMap, String aKey, String aPropertyName){
    aMap.put(aKey, getProperty(aPropertyName));
  }
  
  <span class='keyword'>private</span> String getProperty(String aName){
    <span class='keyword'>return</span> System.getProperty(aName);
  }
} 
</PRE>
<br>
<br><b>Example 3</b>
<p>This example is unusual, in that it uses a <tt>javax.swing.Timer</tt>
to perform a task which is <i>not</i> short lived. This is acceptable,
however, since this <tt>Timer</tt> is combined with a worker thread, and
most of the task is not performed on the event dispatching thread. (Note
the construction of the <tt>Timer</tt> at the end of the constructor using
a <tt>this</tt> reference, and the <tt>actionPerformed</tt> method.)
<p>This is an example of a common requirement - that of performing an intensive,
periodic task, using both a timer and worker thread. Another example would
be a graphical network management application, which polls the state of
many devices every few minutes, and then updates the user interface with
the results.
<br>
<PRE>

<span class='keyword'>package</span> hirondelle.stocks.quotes;

<span class='keyword'>import</span> java.awt.event.*;
<span class='keyword'>import</span> java.awt.*;
<span class='keyword'>import</span> javax.swing.*;
<span class='keyword'>import</span> java.util.logging.*;
<span class='keyword'>import</span> java.util.*;
<span class='keyword'>import</span> java.text.MessageFormat;

<span class='keyword'>import</span> hirondelle.stocks.util.Consts;
<span class='keyword'>import</span> hirondelle.stocks.util.Util;
<span class='keyword'>import</span> hirondelle.stocks.util.Args;
<span class='keyword'>import</span> hirondelle.stocks.util.DataAccessException;
<span class='keyword'>import</span> hirondelle.stocks.util.ui.UiConsts;
<span class='keyword'>import</span> hirondelle.stocks.util.ui.UiUtil;

<span class='keyword'>import</span> hirondelle.stocks.portfolio.CurrentPortfolio;
<span class='keyword'>import</span> hirondelle.stocks.table.QuoteTable;
<span class='keyword'>import</span> hirondelle.stocks.preferences.QuoteTablePreferencesEditor;

<span class='comment'>/**
* Fetch current quote data for the {@link CurrentPortfolio} from a data 
* source on the web.
*
* &lt;P&gt;This action is performed at many different times :
*&lt;ul&gt;
* &lt;li&gt;once upon startup
* &lt;li&gt;periodically, at an interval configured by the user
* &lt;li&gt;when the end user explicitly requests a refresh of quote info
* &lt;li&gt;when the user makes a change to the current portfolio
*&lt;/ul&gt;
*
* &lt;P&gt;This class performs most of its work in a background daemon thread, and uses 
* &lt;tt&gt;EventQueue.invokeLater&lt;/tt&gt; to update the user interface with the result. 
* The user interface remains responsive, regardless of the time taken for its 
* work to complete.
*
* &lt;P&gt;A daemon thread is used since daemon threads do not prevent an application 
* from terminating.
*/</span>
<span class='keyword'>public</span> <span class='keyword'>final</span> <span class='keyword'>class</span> FetchQuotesAction <span class='keyword'>extends</span> AbstractAction <span class='keyword'>implements</span> Observer {

  <span class='comment'>/**
  * Constructor.
  *  
  * @param aCurrentPortfolio an &lt;tt&gt;Observable&lt;/tt&gt; which notifies this 
  * object when the {@link CurrentPortfolio} is changed
  * @param aQuoteTablePrefEditor allows this class to read the user preference
  * for the frequency of periodic fetches
  * @param aQuoteTable a GUI element which is updated when a fetch is performed
  * @param aSummaryView a GUI element which is updated when a fetch is performed
  */</span>
  <span class='keyword'>public</span> FetchQuotesAction (
    CurrentPortfolio aCurrentPortfolio, 
    QuoteTablePreferencesEditor aQuoteTablePrefEditor, 
    QuoteTable aQuoteTable, 
    SummaryView aSummaryView
  ) {
    <span class='keyword'>super</span>(<span class='literal'>"Update"</span>, UiUtil.getImageIcon(<span class='literal'>"/toolbarButtonGraphics/general/Refresh"</span>)); 
    Args.checkForNull(aQuoteTable);
    Args.checkForNull(aSummaryView);
    fCurrentPortfolio = aCurrentPortfolio;
    
    fQuoteTablePrefEditor = aQuoteTablePrefEditor;
    
    fQuoteTable = aQuoteTable;
    fSummaryView = aSummaryView;
    
    putValue(SHORT_DESCRIPTION, <span class='literal'>"Fetch updated stock quotes from web"</span>);
    putValue(
      ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F5, UiConsts.NO_KEYSTROKE_MASK)
    );      
    putValue(
      LONG_DESCRIPTION, 
      <span class='literal'>"Retrieves fresh stock quotes and displays it to the user in a table."</span>
    );
    putValue(MNEMONIC_KEY, <span class='keyword'>new</span> Integer(KeyEvent.VK_U) );    

    fUpdateFreq = fQuoteTablePrefEditor.getUpdateFrequency();
  }

  <span class='comment'>/**
  * Start an internal timer.
  * &lt;P&gt;This method must be called immediately after calling the constructor. 
  * Since this operation uses a 'this' reference, it cannot be included in the 
  * constructor itself.  
  */</span>
  <span class='keyword'>public</span> <span class='keyword'>void</span> startTimer(){
    fQuoteTablePrefEditor.addObserver(<span class='keyword'>this</span>);
    fCurrentPortfolio.addObserver(<span class='keyword'>this</span>);
    fTimer = <span class='keyword'>new</span> javax.swing.Timer(fUpdateFreq * CONVERSION_FACTOR, <span class='keyword'>this</span>);
    fTimer.start();
  }

  <span class='comment'>/** Fetch quotes from the web for the &lt;tt&gt;CurrentPortfolio&lt;/tt&gt;.  */</span>
  <span class='keyword'>public</span> <span class='keyword'>void</span> actionPerformed(ActionEvent e) {
    fLogger.info(<span class='literal'>"Fetching quotes from web."</span>);
    fSummaryView.showStatusMessage(<span class='literal'>"Fetching quotes..."</span>);
    Thread worker = <span class='keyword'>new</span> Thread(<span class='keyword'>new</span> HardWorker());
    worker.setDaemon(<span class='keyword'>true</span>);
    worker.start();
  }
  
  <span class='comment'>/**
  * Listens for changes to the &lt;tt&gt;CurrentPortfolio&lt;/tt&gt; or the user
  * preference for update frequency, and calls {@link #actionPerformed}.
  *
  * &lt;P&gt;In the case of changes to the update frequency, &lt;tt&gt;actionPerformed&lt;/tt&gt; is 
  * called only if the udate frequency has been assigned a new value.
  */</span>
  <span class='keyword'>public</span> <span class='keyword'>void</span> update(Observable aPublisher, Object aData) {
    fLogger.fine(<span class='literal'>"Notified ..."</span>);
    <span class='keyword'>if</span> ( aPublisher == fQuoteTablePrefEditor ) {
      fLogger.fine(<span class='literal'>"By StocksTablePrefEditor..."</span>);
      <span class='keyword'>boolean</span> hasChangedFreq = fQuoteTablePrefEditor.getUpdateFrequency()!= fUpdateFreq;
      <span class='keyword'>if</span> ( hasChangedFreq ) restartTimer();
    }
    <span class='keyword'>else</span> {
      fLogger.fine(<span class='literal'>"By Current Portfolio..."</span>);
      actionPerformed(<span class='keyword'>null</span>);
    }
  } 

  <span class='comment'>// PRIVATE //
</span>
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>final</span> Logger fLogger = Util.getLogger(FetchQuotesAction.<span class='keyword'>class</span>);
  
  <span class='comment'>/**
  * The set of {@link Stock} objects in which the user 
  * is currently interested.
  */</span>
  <span class='keyword'>private</span> CurrentPortfolio fCurrentPortfolio;
  
  <span class='comment'>/**
  * Edits user preferences attached to a table which presents quote data, and 
  * allows read-only programmatic access to such preferences.
  */</span>
  <span class='keyword'>private</span> QuoteTablePreferencesEditor fQuoteTablePrefEditor;
  
  <span class='comment'>/**
  * GUI element which is updated whenever a new set of quotes is obtained.
  */</span>
  <span class='keyword'>private</span> QuoteTable fQuoteTable;

  <span class='comment'>/**
  * GUI element which is updated whenever a new set of quotes is obtained.
  */</span>
  <span class='keyword'>private</span> SummaryView fSummaryView;
  
  <span class='comment'>/**
  * Periodically fetches quote data.
  *
  * &lt;P&gt;Use of a Swing Timer is acceptable here, in spite of the fact that the task
  * takes a long time to complete, since the task does &lt;em&gt;not&lt;/em&gt; in fact get 
  * executed on the event-dispatch thread (See below.)
  */</span>
  <span class='keyword'>private</span> javax.swing.Timer fTimer;
  
  <span class='comment'>/**
  * The number of minutes to wait between fetches of quote information.
  */</span>
  <span class='keyword'>private</span> <span class='keyword'>int</span> fUpdateFreq;
  
  <span class='keyword'>private</span> <span class='keyword'>static</span> <span class='keyword'>final</span> <span class='keyword'>int</span> CONVERSION_FACTOR = 
    Consts.MILLISECONDS_PER_SECOND * Consts.SECONDS_PER_MINUTE
  ;

  <span class='comment'>/**
  * Perform the fetch, and update the GUI elements using the event dispatch 
  * thread.
  *
  * &lt;P&gt;Should be run as a daemon thread, such that it never prevents the application
  * from exiting.
  */</span>
  <span class='keyword'>private</span> <span class='keyword'>final</span> <span class='keyword'>class</span> HardWorker <span class='keyword'>implements</span> Runnable {
    <span class='keyword'>public</span> <span class='keyword'>void</span> run() {
      <span class='comment'>//simulateLongDelay();
</span>      <span class='keyword'>try</span> {
        java.util.List&lt;Quote&gt; quotes = fCurrentPortfolio.getPortfolio().getQuotes();
        EventQueue.invokeLater( <span class='keyword'>new</span> GuiUpdater(quotes) );
      }
      <span class='keyword'>catch</span>(DataAccessException ex) {
        EventQueue.invokeLater( <span class='keyword'>new</span> GuiUpdater(ex) );
      }
    }
  }
  
  <span class='comment'>/**
  * Update the user interface after the fetch completes.
  *
  * &lt;P&gt;Must be run on the event dispatch thread. If the fetch fails for any reason, 
  * then any current quote data is left as is, and an error message is displayed in 
  * a status message.
  */</span>
  <span class='keyword'>private</span> <span class='keyword'>final</span> <span class='keyword'>class</span> GuiUpdater <span class='keyword'>implements</span> Runnable {
    GuiUpdater( java.util.List&lt;Quote&gt; aQuotes ){
      fQuotes = aQuotes;
    }
    GuiUpdater(DataAccessException ex){
      <span class='comment'>//the exception is not used in this implementation
</span>    }
    <span class='keyword'>public</span> <span class='keyword'>void</span> run(){
      <span class='keyword'>if</span> (fQuotes != <span class='keyword'>null</span>){
        fQuoteTable.setQuoteTable( fQuotes );
        fSummaryView.setQuotes( fQuotes );
        StringBuilder warning = <span class='keyword'>new</span> StringBuilder();
        <span class='keyword'>if</span> ( hasNoZeroPrices(warning) ){
          fSummaryView.showStatusMessage(<span class='literal'>"Done."</span>);
        }
        <span class='keyword'>else</span> {
          fSummaryView.showStatusMessage(warning.toString());
        }
      }
      <span class='keyword'>else</span> {
        fSummaryView.showStatusMessage(<span class='literal'>"Failed - Please connect to the web."</span>);
      }
    }
    <span class='keyword'>private</span> java.util.List&lt;Quote&gt; fQuotes;
    <span class='keyword'>private</span> MessageFormat fTickerWarningFormat = 
      <span class='keyword'>new</span> MessageFormat(<span class='literal'>"Warning - no price for ticker {0} ({1})"</span>)
    ;
    <span class='keyword'>private</span> <span class='keyword'>boolean</span> hasNoZeroPrices(StringBuilder aMessage){
      <span class='keyword'>for</span>(Quote quote: fQuotes){
        <span class='keyword'>if</span> ( Util.isZeroMoney(quote.getPrice()) ) {
          Object[] params = {
            quote.getStock().getTicker(), 
            quote.getStock().getExchange()
          };
          aMessage.append(fTickerWarningFormat.format(params));
          <span class='keyword'>return</span> <span class='keyword'>false</span>;
        }
      }
      <span class='keyword'>return</span> <span class='keyword'>true</span>;
    }
  }
  
  <span class='comment'>/** Use for testing purposes only.  */</span>
  <span class='keyword'>private</span> <span class='keyword'>void</span> simulateLongDelay(){
    <span class='keyword'>try</span> {
      Thread.sleep(<span class='literal'>20000</span>);
    }
    <span class='keyword'>catch</span> (InterruptedException ex) {
      System.out.println(ex);
    }
  }
  
  <span class='keyword'>private</span> <span class='keyword'>void</span> restartTimer(){
    fLogger.fine(<span class='literal'>"Resetting initial delay and delay to: "</span> + fUpdateFreq + <span class='literal'>" minutes."</span>);
    fUpdateFreq = fQuoteTablePrefEditor.getUpdateFrequency();
    fTimer.setInitialDelay(fUpdateFreq * CONVERSION_FACTOR);
    fTimer.setDelay(fUpdateFreq * CONVERSION_FACTOR);
    fLogger.fine(<span class='literal'>"Cancelling pending tasks, and restarting timer..."</span>);
    fTimer.restart();
  }
}
 
</PRE>
<br>
<br>
<br>

</div>




<div class='topic-section'>See Also :</div>
<div class='main-body'>
 
  
  <a href='TopicActionc150-2.html?Id=54'>Use Timer to schedule periodic tasks </a> <br>
 
  
  <a href='TopicAction3abb-2.html?Id=153'>Swing threads</a> <br>
 
</div>


<div class='topic-section'>Would you use this technique?</div>
<div class='main-body'>
  
  <form action="http://www.javapractices.com/vote/AddVoteAction.do" method='post'>
    Yes<input type='radio' name='Choice' value='Y' >
    &nbsp;&nbsp;No<input type='radio' name='Choice' value='N'>
    &nbsp;&nbsp;Undecided<input type='radio' name='Choice' value="?" >
    &nbsp;&nbsp;<input type=submit value="Vote" >
    <input type='hidden' name='Operation' value='Apply'>
    <input type='hidden' name='TopicId' value='160'>
  </form>
</div>

<div style='height:10.0em;'></div>

 
 
</div>

  

 





<div align='center' class='legalese'>  
&copy; 2011 Hirondelle Systems |
<a href='../source/SourceAction-2.html'><b>Source Code</b></a><IMG class='no-margin' SRC="../images/goldstar.gif" ALT=""> |
<a href="mailto:webmaster@javapractices.com">Contact</a> |
<a href="http://creativecommons.org/licenses/by-nc-sa/1.0/">License</a> |
<a href='../apps/cjp.rss'>RSS</a>
<!-- ukey="2AC36CD2" -->
<!-- ckey="16DF3D87" -->
<br>

 Individual code snippets can be used under this <a href='../LICENSE.txt'>BSD license</a> - Last updated on June 6, 2010.<br>
 Over 150,000 unique IPs last month - <span title='Java Practices 2.6.5, Mon May 16 00:00:00 EDT 2011'>Built with</span> <a href='http://www.web4j.com/'>WEB4J</a>.<br>
 - In Memoriam : Bill Dirani -
</div>

<script src="../../www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "UA-2633428-1";
urchinTracker();
</script>



</body>

<!-- Mirrored from www.javapractices.com/topic/TopicAction.do;jsessionid=4FCCB481C702D708A7360133D128E359?Id=160 by HTTrack Website Copier/3.x [XR&CO'2010], Sun, 12 Jun 2011 17:27:18 GMT -->
<!-- Added by HTTrack --><meta http-equiv="content-type" content="text/html;charset=UTF-8"><!-- /Added by HTTrack -->
</html>
